﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using HeapModel;
using HeapModel.ProgInfo;
using HeapModel.RegionPartition;

namespace MetricExtractor
{
	/// <summary>
	/// Extracts metrics about basic graph theoretic properties.
	/// 
	/// TreeFields: Number of abstract locations that always contain tree edges.
	/// CrossFields: Number of abstract locations that may contain cross edges.
	/// BackFields: Number of abstract locations that may contain back edges.
	/// 
	/// CrossFieldsImmutable: Number of abstract locations that may contain cross edges but only to immutable objects.
	/// CrossFieldsSingleton: Number of abstract locations that may contain cross edges but only to singleton objects.
	/// CrossFieldsGlobalRegion: Number of abstract locations that may contain cross edges but only to single region type objects (i.e. these types always appear in a single region).
	/// CrossFieldLocal: Number of abstract locations s.t. it is a cross edges but the unique immediate parent of this object is the dominator of all other regions that share.
	/// 
	/// REPEAT ALL OF THESE FOR REGION INFO AS WELL
	/// </summary>
	internal sealed class BasicHeapStructureMetrics
	{
		public static string[] StructMetricsPerEdge = { "TreeEdges", "CrossEdges", "BackEdges", "CrossEdgeImmutable", "CrossEdgeUnique", "CrossEdgeGlobal", "CrossEdgeLocal", "UnclassifiedCrossEdges" };

		private int TreeEdgeCount = 0;
		private int CrossEdgeCount = 0;
		private int BackEdgeCount = 0;
		private int CrossImmCount = 0;
		private int CrossSingletonCount = 0;
		private int CrossGlobalCount = 0;
		private int CrossLocalCount = 0;
		private int UnclassifiedCrossEdgeCount = 0;

		internal BasicHeapStructureMetrics()
		{
		}

		internal void Extract(RegionGraph hg, Dictionary<string, Tuple<bool, int>> singleregiontypes)
		{
			double trgns = hg.FinalSuccMap.Values.Aggregate(0.0, (double dv, List<RegionEdgeInfo> refl) => dv + refl.Count((RegionEdgeInfo rei) => rei.Target != null));

			//get info based on edges
			foreach(RegionNode hr in hg.Regions)
			{
				if(hr.RegionTypes.Count == 1 && hr.RegionTypes.First().IsSpecialNullType)
					continue;

				foreach(RegionEdgeInfo hrs in hg.FinalSuccMap[hr])
				{
					if(hrs.Target == null)
						continue;

					string name = hrs.Offset.ToString();

					if(hrs.EdgeOrientation == RegionEdgeInfo.Orientation.Back)
					{
						this.BackEdgeCount++;
					}
					else if(hrs.EdgeOrientation == RegionEdgeInfo.Orientation.Cross)
					{
						this.CrossEdgeCount++;

						bool immutabletarget = ExtractionSupport.AllTypesImmutable(hrs.Target.RegionTypes);
						if(immutabletarget)
							this.CrossImmCount++;

						bool allsingleregiontypes = hrs.Target.RegionTypes.All((TypeIdentifier ttid) => singleregiontypes[ttid.NameValue].Item1 && singleregiontypes[ttid.NameValue].Item2 == 1);
						if(allsingleregiontypes)
							this.CrossSingletonCount++;

						bool allglobalregiontypes = hrs.Target.RegionTypes.All((TypeIdentifier ttid) => singleregiontypes[ttid.NameValue].Item1 && singleregiontypes[ttid.NameValue].Item2 != 1);
						if(allglobalregiontypes)
							this.CrossGlobalCount++;

						RegionNode trgtrgn = hrs.Target;
						bool localdom = true;
						foreach(RegionEdgeInfo trgtref in hg.FinalPredMap[trgtrgn])
						{
							if(trgtref.Source == null)
								continue;

							bool sameparent = trgtref.Source == hr;
							bool onehopviatref = hg.FinalSuccMap[hr].Any((RegionEdgeInfo outer) => outer.Target == trgtref.Source && hg.FinalPredMap[outer.Target].Count((RegionEdgeInfo iihrs) => iihrs.Source != null) == 1);
							bool onehopviahr = hg.FinalSuccMap[trgtref.Source].Any((RegionEdgeInfo outer) => outer.Target == hr && hg.FinalPredMap[hr].Count((RegionEdgeInfo iihrs) => iihrs.Source != null) == 1);
							bool both = false;

							if(hg.FinalPredMap[trgtref.Source].Count((RegionEdgeInfo iihrs) => iihrs.Source != null) == 1 && hg.FinalPredMap[hr].Count((RegionEdgeInfo iihrs) => iihrs.Source != null) == 1)
								both = (hg.FinalPredMap[trgtref.Source].First((RegionEdgeInfo iihrs) => iihrs.Source != null).Source == hg.FinalPredMap[hr].First((RegionEdgeInfo iihrs) => iihrs.Source != null).Source);
							
							localdom &= (sameparent | onehopviatref | onehopviahr | both);
						}

						if(localdom)
							this.CrossLocalCount++;

						if(!(immutabletarget | allsingleregiontypes | allglobalregiontypes | localdom))
							this.UnclassifiedCrossEdgeCount++;
					}
					else
					{
						this.TreeEdgeCount++; 
					}
				}
			}
		}

		internal string CSVFormat()
		{ return String.Format("{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}", this.TreeEdgeCount, this.CrossEdgeCount, this.BackEdgeCount, this.CrossImmCount, this.CrossSingletonCount, this.CrossGlobalCount, this.CrossLocalCount, this.UnclassifiedCrossEdgeCount); }
	}
}
